package com.fit.utils.se;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/**
 * 二维码生成工具
 * @author hcl
 * 依赖：core-3.3.0.jar、javase-3.2.1.jar
 */
public class QRUtil {

	/**
	 * 编码格式
	 */
	public static String CODE = "utf-8";

	/**
	 * 前景颜色（十六进制）,黑色
	 */
	public static int FOREGROUND = 0xff000000;
	/**
	 * 背景颜色（十六进制），白色
	 */
	public static int BACKGROUND = 0xFFFFFFFF;

	/**
	 * 后缀
	 */
	public static String SUFFIX = "png";

	/**
	 * 生成条形码,写到文件中
	 * 
	 * @param content
	 * @param width
	 * @param height
	 * @param file
	 * @throws IOException
	 * @author hcl
	 * @throws WriterException
	 */
	public static void barCode(String content, Integer width, Integer height,
			File file) throws IOException, WriterException {
		BufferedImage image = barCode(content, width, height);
		ImageIO.write(image, SUFFIX, file);
	}

	/**
	 * 生成条形码,写到输出流中
	 * 
	 * @param content
	 * @param width
	 * @param height
	 * @param out
	 * @throws IOException
	 * @author hcl
	 * @throws WriterException
	 */
	public static void barCode(String content, Integer width, Integer height,
			OutputStream out) throws IOException, WriterException {
		try {
			BufferedImage image = barCode(content, width, height);
			ImageIO.write(image, SUFFIX, out);
		} finally {
			IOUtil.close(out);
		}
	}

	/**
	 * 生成一维码（128）
	 * 
	 * @author wuhongbo
	 * @param str
	 * @param width
	 * @param height
	 * @return
	 * @throws WriterException
	 */
	private static BufferedImage barCode(String content, Integer width,
			Integer height) throws WriterException {
		if (width == null || width < 200) {
			width = 200;
		}
		if (height == null || height < 50) {
			height = 50;
		}
		// 文字编码
		Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
		hints.put(EncodeHintType.CHARACTER_SET, CODE);
		BitMatrix bitMatrix = new MultiFormatWriter().encode(content,
				BarcodeFormat.CODE_128, width, height, hints);
		return toBufferedImage(bitMatrix);
	}

	/**
	 * 生成二维码，写到文件中
	 * 
	 * @param content
	 * @param size
	 * @param file
	 * @throws IOException
	 * @author hcl
	 * @throws WriterException
	 */
	public static void qrCode(String content, Integer size, File file)
			throws IOException, WriterException {
		BufferedImage image = qrCode(content, size);
		ImageIO.write(image, SUFFIX, file);
	}

	/**
	 * 生成二维码，写到输出流中
	 * 
	 * @param content
	 * @param size
	 * @param file
	 * @throws IOException
	 * @throws WriterException
	 */
	public static void qrCode(String content, Integer size, OutputStream out)
			throws IOException, WriterException {
		try {
			BufferedImage image = qrCode(content, size);
			ImageIO.write(image, SUFFIX, out);
		} finally {
			IOUtil.close(out);
		}
	}

	/**
	 * 生成带logo的二维码
	 * 
	 * @param content
	 * @param size
	 * @param logoIn
	 * @param out
	 * @throws WriterException
	 * @throws IOException
	 */
	public static void qrCode(String content, Integer size, InputStream logoIn,
			OutputStream out) throws WriterException, IOException {
		try {
			BufferedImage image = qrCode(content, size);
			addLogo(image, logoIn);
			ImageIO.write(image, SUFFIX, out);
		} finally {
			IOUtil.close(out,logoIn);
		}
	}

	/**
	 * 生成带logo的二维码
	 * 
	 * @param content
	 * @param size
	 * @param logoIn
	 * @param out
	 * @throws WriterException
	 * @throws IOException
	 */
	public static void qrCode(String content, Integer size, File logoFile,
			OutputStream out) throws WriterException, IOException {
		try {
			BufferedImage image = qrCode(content, size);
			addLogo(image, new FileInputStream(logoFile));
			ImageIO.write(image, SUFFIX, out);
		} finally {
			IOUtil.close(out);
		}
	}

	/**
	 * 给图片添加logo
	 * 
	 * @param image
	 * @param logoIn
	 * @throws IOException
	 */
	private static void addLogo(BufferedImage image, InputStream logoIn)
			throws IOException {
		try {
			//内边距
			int paing = (int)(image.getWidth()*0.03);
			//logo与最大外面大图片的比例
			Double logoProportion = 0.2;
			//圆角比例
			Double radiusProportion = 0.3;
			
			//加载logo图片
			BufferedImage logo = ImageIO.read(logoIn);

			//二维码图片的宽度和高度
			int imgW = image.getWidth();
			int imgH = image.getHeight();
			//logo图片缩小后的宽度和高度
			int logoW = (int) (imgW * logoProportion);
			int logoH = (int) (imgH * logoProportion);
			//logo背景的宽度和高度
			int newLogoImgW = logoW + paing;
			int newLogoImgH = logoH + paing;

			//缩小图片
			BufferedImage logoLessen = new BufferedImage(logoW, logoH,  BufferedImage.TYPE_INT_RGB);
			Graphics2D logoLessengs = logoLessen.createGraphics();
			// 绘制缩小后的图 
			logoLessengs.drawImage(logo, 0, 0, logoW, logoH, null);
			logoLessengs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			
			//设置logo圆角
			int radius = (int)(logoW * radiusProportion);
			
			// 圆角
			logoLessengs.setComposite(AlphaComposite.Src);
			logoLessengs.setColor(Color.WHITE);
			RoundRectangle2D round = new RoundRectangle2D.Double(0, 0, logoW, logoH, radius , radius);
			Area clear = new Area(new Rectangle(0, 0, logoW, logoH));
			clear.subtract(new Area(round));
			// 抗锯齿
			logoLessengs.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			logoLessengs.fill(clear);
			logoLessengs.setComposite(AlphaComposite.SrcAtop);
			//设置图片边框
			logoLessengs.setColor(Color.GRAY);
			logoLessengs.setStroke(new BasicStroke(0.3f));
			logoLessengs.drawRoundRect(0, 0, logoW-1, logoH - 1, radius, radius);
			logoLessengs.dispose();
			
			//背景填充为白色
			BufferedImage bgInmg = new BufferedImage(newLogoImgW, newLogoImgH, BufferedImage.TYPE_INT_ARGB);
			Graphics2D bggs = bgInmg.createGraphics();
			bggs.setComposite(AlphaComposite.Src);
			bggs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			bggs.setColor(Color.WHITE);
			bggs.fill(new RoundRectangle2D.Float(0, 0, newLogoImgW, newLogoImgH , (int)(newLogoImgW * radiusProportion),(int)(newLogoImgW * radiusProportion)));
			bggs.setComposite(AlphaComposite.SrcAtop);
			bggs.drawImage(logoLessen, paing / 2, paing / 2,null);
			
			bggs.dispose();
			
			Graphics2D imgGs = image.createGraphics();
			imgGs.drawImage(bgInmg, (imgW - newLogoImgW) / 2, (imgH - newLogoImgH) / 2, null);
			
			imgGs.dispose();
			logo.flush();
		} finally {
			IOUtil.close(logoIn);
		}

	}

	/**
	 * 生成RQ二维码
	 * 
	 * @author wuhongbo
	 * @param str
	 *            内容
	 * @param height
	 *            高度（px）
	 * @throws WriterException
	 * 
	 */
	private static BufferedImage qrCode(String str, Integer height)
			throws WriterException {
		if (height == null || height <= 0) {
			height = 200;
		}
		// 文字编码
		Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
		hints.put(EncodeHintType.CHARACTER_SET, CODE);
		/**
		 * 二维码有一个容错率的概念。
		 * 容错率是指二维码在正常识别的前提下可被遮挡的部分占总二维码面积的百分比。容错率越高，可被遮挡的面积越大，二维码图案也越复杂。
		 * 目前容错率可分为L,M,Q,H四个等级，分别表示7%,15%,25%,30%搜索 但是，二维码的复杂程度不全取决于二维码的容错率。
		 * 在容错率一定的情况下
		 * ，二维码包含的信息越多，二维码也会越复杂。目前，鉴于国产手机摄像头的辨识能力，建议生成二维码时，不要超过150个字符。（
		 * 二维码太复杂也影响构图美观） 对目前主流手机，在绝大多数扫描场景下，容错率越高，越容易被快速扫描!!!
		 */
		// 二维码容错率，分四个等级：L、M、Q、H
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		// 设置边框白的宽度
		hints.put(EncodeHintType.MARGIN, 0);
		BitMatrix bitMatrix = new MultiFormatWriter().encode(str,
				BarcodeFormat.QR_CODE, height, height, hints);
		return toBufferedImage(bitMatrix);
	}

	/**
	 * 转换成图片
	 * 
	 * @author wuhongbo
	 * @param matrix
	 * @return
	 */
	private static BufferedImage toBufferedImage(BitMatrix matrix) {
		int width = matrix.getWidth();
		int height = matrix.getHeight();
		BufferedImage image = new BufferedImage(width, height,
				BufferedImage.TYPE_INT_ARGB);
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				image.setRGB(x, y, matrix.get(x, y) ? FOREGROUND : BACKGROUND);
			}
		}
		return image;
	}

	/**
	 * 解码(二维、一维均可)
	 * 
	 * @param file
	 * @return
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public static String decode(File file) throws IOException,
			NotFoundException {
		BufferedImage image = ImageIO.read(file);
		LuminanceSource source = new BufferedImageLuminanceSource(image);
		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
		// 解码设置编码方式为：utf-8，
		Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
		hints.put(DecodeHintType.CHARACTER_SET, CODE);
		Result result = new MultiFormatReader().decode(bitmap, hints);
		return result.getText();
	}

	/**
	 * 解码(二维、一维均可)
	 * 
	 * @param in
	 * @return
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public static String decode(InputStream in) throws IOException,NotFoundException {
		BufferedImage image = ImageIO.read(in);
		if(image == null){
			return null;
		}
		LuminanceSource source = new BufferedImageLuminanceSource(image);
		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
		// 解码设置编码方式为：utf-8，
		Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
		hints.put(DecodeHintType.CHARACTER_SET, CODE);
		Result result = new MultiFormatReader().decode(bitmap, hints);
		return result.getText();
	}

}